#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <time.h>
#include <unistd.h>
#include "campo.h"
Campo alocarCampo(int);
void montarCampo(Campo);
void gerarBorda(Campo);
Casa * getCasa(Campo, Pos);
void setCasa(Campo, Pos, Casa);
void desalocarCampo(Campo);
void imprimirCampo(Campo);
int ehBomba(TipoCasa);
int ehNumero(TipoCasa);
int ehBorda(TipoCasa);
int bombasFechadasAoRedor(Casa*, Pos, Campo);
int casasInvisiveisAoRedor(Pos, Campo);

Casa * getCasa(Campo campo, Pos pos) {
	return &campo.tabuleiro[pos.m + 1][pos.n + 1];
}

void setCasa(Campo campo, Pos pos, Casa casa) {
	campo.tabuleiro[pos.m + 1][pos.n + 1] = casa;
}

Campo alocarCampo(int n) {
	Campo campo;
	campo.tamanho = n;
	campo.tabuleiro = malloc(sizeof(Casa) * (n + 2));

	if (campo.tabuleiro == NULL) {
		campo.tamanho = 0;
		return campo;
	}

	int i;
	for (i = 0; i <= n + 1; i++) {
		campo.tabuleiro[i] = malloc(sizeof(Casa) * (n + 2));

		if (campo.tabuleiro[i] == NULL) {
			campo.tamanho = 0;
			return campo;
		}

	}

	srand(time(NULL) * getpid());
	campo.totBombas = 0;
	while (!campo.totBombas || campo.totBombas >= (pow(campo.tamanho, 2) / 2)) {
		campo.totBombas = (int) (((double) rand() / RAND_MAX) * (pow(
				campo.tamanho, 2) / 2));
		if (!(campo.totBombas % 2)) {
			campo.totBombas++;
		}
	}
	Casa casa;
	casa.tipoCasa = zero;
	casa.visivel = 1;
	Pos pos;
	for (pos.m = -1; pos.m <= campo.tamanho; pos.m++) {
		for (pos.n = -1; pos.n <= campo.tamanho; pos.n++) {
			setCasa(campo, pos, casa);
		}
	}

	return campo;
}

void gerarBorda(Campo campo) {
	Pos pos;
	Casa casa;
	casa.visivel = 1;
	casa.tipoCasa = bordaHorizontal;
	for (pos.n = 0; pos.n < campo.tamanho; pos.n++) {
		pos.m = -1;
		setCasa(campo, pos, casa);
		pos.m = campo.tamanho;
		setCasa(campo, pos, casa);
	}

	casa.tipoCasa = bordaVertical;
	for (pos.m = 0; pos.m < campo.tamanho; pos.m++) {
		pos.n = -1;
		setCasa(campo, pos, casa);
		pos.n = campo.tamanho;
		setCasa(campo, pos, casa);
	}

	pos.m = -1;
	casa.tipoCasa = bordaSupEsq;
	pos.n = -1;
	setCasa(campo, pos, casa);

	pos.n = campo.tamanho;
	casa.tipoCasa = bordaSupDir;
	setCasa(campo, pos, casa);

	pos.m = campo.tamanho;
	casa.tipoCasa = bordaInfDir;
	setCasa(campo, pos, casa);

	pos.n = -1;
	casa.tipoCasa = bordaInfEsq;
	setCasa(campo, pos, casa);
}

void montarCampo(Campo campo) {

	gerarBorda(campo);
	Pos posBomba;
	Casa casa;
	int totalBombas = 0;
	while (totalBombas != campo.totBombas) {
		posBomba.m = (int) (((double) rand() / RAND_MAX) * (campo.tamanho - 1));
		posBomba.n = (int) (((double) rand() / RAND_MAX) * (campo.tamanho - 1));
		casa = *getCasa(campo, posBomba);
		if ((!ehBorda(casa.tipoCasa)) && (!ehBomba(casa.tipoCasa))) {
			casa.tipoCasa = bomba;
			casa.visivel = 0;
			setCasa(campo, posBomba, casa);
			totalBombas++;
		}
	}
	Pos posCasaNaoBomba;
	for (posCasaNaoBomba.m = 0; posCasaNaoBomba.m < campo.tamanho; posCasaNaoBomba.m++) {
		for (posCasaNaoBomba.n = 0; posCasaNaoBomba.n < campo.tamanho; posCasaNaoBomba.n++) {
			casa = *getCasa(campo, posCasaNaoBomba);
			if (ehBomba(casa.tipoCasa)) {
				continue;
			}

			int bombas = 0;
			posBomba.m = posCasaNaoBomba.m - 1;
			for (posBomba.n = posCasaNaoBomba.n - 1; posBomba.n
					<= posCasaNaoBomba.n + 1; posBomba.n++) {
				if (ehBomba((*getCasa(campo, posBomba)).tipoCasa)) {
					bombas++;
				}
			}

			posBomba.m = posCasaNaoBomba.m + 1;
			for (posBomba.n = posCasaNaoBomba.n - 1; posBomba.n
					<= posCasaNaoBomba.n + 1; posBomba.n++) {
				if (ehBomba((*getCasa(campo, posBomba)).tipoCasa)) {
					bombas++;
				}
			}

			posBomba.m = posCasaNaoBomba.m;
			posBomba.n = posCasaNaoBomba.n - 1;
			bombas += (ehBomba((*getCasa(campo, posBomba)).tipoCasa)) ? 1 : 0;
			posBomba.n = posCasaNaoBomba.n + 1;
			bombas += (ehBomba((*getCasa(campo, posBomba)).tipoCasa)) ? 1 : 0;
			casa.tipoCasa = bombas;
			casa.visivel = 0;
			setCasa(campo, posCasaNaoBomba, casa);
		}
	}
}

void desalocarCampo(Campo campo) {
	int i;
	for (i = 0; i <= campo.tamanho + 2; i++) {
		free(campo.tabuleiro[i]);
	}
	free(campo.tabuleiro);
}

void imprimirCampo(Campo campo) {
	int i, j;
	Pos pos;
	printf("      ");
	for (i = 0; i < campo.tamanho; i++) {
		if (i < 9) {
			printf("%d  ", i);
		} else {
			printf("%d ", i);
		}
	}
	printf("\n");
	for (i = -1; i <= campo.tamanho; i++) {
		pos.m = i;
		if (i >= 0 && i < campo.tamanho) {
			printf("%2d ", i);
		} else {
			printf("   ");
		}
		for (j = -1; j <= campo.tamanho; j++) {
			pos.n = j;
			char c;
			if (!(*getCasa(campo, pos)).visivel) {
				c = '-';
			} else {
				switch ((*getCasa(campo, pos)).tipoCasa) {
				case zero: {
					c = ' ';
					break;
				}
				case uma: {
					c = '1';
					break;
				}
				case duas: {
					c = '2';
					break;
				}
				case tres: {
					c = '3';
					break;
				}
				case quatro: {
					c = '4';
					break;
				}
				case cinco: {
					c = '5';
					break;
				}
				case seis: {
					c = '6';
					break;
				}
				case sete: {
					c = '7';
					break;
				}
				case oito: {
					c = '8';
					break;
				}
				case bomba: {
					c = '*';
					break;
				}
				case bombaHumano: {
					c = 'H';
					break;
				}
				case bombaComputador: {
					c = 'C';
					break;
				}
#if __WIN32
				case bordaHorizontal: {
					c = 196;
					break;
				}
				case bordaVertical: {
					c = 179;
					break;
				}
				case bordaSupEsq: {
					c = 218;
					break;
				}
				case bordaSupDir: {
					c = 191;
					break;
				}
				case bordaInfEsq: {
					c = 192;
					break;
				}
				case bordaInfDir: {
					c = 217;
					break;
				}
#else
					default: {
						c = '#';
						break;
					}
#endif
				}
			}

#if __WIN32
			char horBord = 196;
#else
			char horBord = '#';
#endif
			if (j != campo.tamanho) {
				if ((i == -1) || (i == campo.tamanho)) {
					printf("%c%c%c", c, horBord, horBord);
				} else {
					printf("%c  ", c);
				}
			} else
				printf("%c ", c);
		}

		if (i >= 0 && i < campo.tamanho)
			printf("%d", i);

		printf("\n");
	}
	printf("      ");
	for (i = 0; i < campo.tamanho; i++) {
		if (i < 9) {
			printf("%d  ", i);
		} else {
			printf("%d ", i);
		}
	}
	printf("\n");
}

int ehBomba(TipoCasa tipo) {
	return ((tipo >= bomba) && (tipo <= bombaComputador));
}

int ehNumero(TipoCasa tipo) {
	return ((tipo > zero) && (tipo <= oito));
}

int ehBorda(TipoCasa tipo) {
	return ((tipo >= bordaHorizontal) && (tipo <= bordaInfDir));
}

int bombasFechadasAoRedor(Casa * casaAtual, Pos posicaoCasa, Campo campo) {
	int qntDeBombasAoRedor = casaAtual->tipoCasa;

	if (!qntDeBombasAoRedor) {
		return 0;
	}
	Casa * casaAdjacente;
	Pos posAdjacente;

	posAdjacente.m = posicaoCasa.m - 1;
	for (posAdjacente.n = posicaoCasa.n - 1; posAdjacente.n <= posicaoCasa.n
			+ 1; posAdjacente.n++) {
		casaAdjacente = getCasa(campo, posAdjacente);
		if (casaAdjacente->visivel && ehBomba(casaAdjacente->tipoCasa))
			qntDeBombasAoRedor--;
	}

	posAdjacente.m = posicaoCasa.m + 1;
	for (posAdjacente.n = posicaoCasa.n - 1; posAdjacente.n <= posicaoCasa.n
			+ 1; posAdjacente.n++) {
		casaAdjacente = getCasa(campo, posAdjacente);
		if (casaAdjacente->visivel && ehBomba(casaAdjacente->tipoCasa))
			qntDeBombasAoRedor--;
	}

	posAdjacente.m = posicaoCasa.m;
	posAdjacente.n = posicaoCasa.n - 1;
	casaAdjacente = getCasa(campo, posAdjacente);
	if (casaAdjacente->visivel && ehBomba(casaAdjacente->tipoCasa))
		qntDeBombasAoRedor--;

	posAdjacente.n = posicaoCasa.n + 1;
	casaAdjacente = getCasa(campo, posAdjacente);
	if (casaAdjacente->visivel && ehBomba(casaAdjacente->tipoCasa))
		qntDeBombasAoRedor--;

	return qntDeBombasAoRedor;
}

int casasInvisiveisAoRedor(Pos posicaoCasa, Campo campo) {
	int casasInvisiveis = 0;
	Casa * casaAdjacente;
	Pos posAdjacente;

	posAdjacente.m = posicaoCasa.m - 1;
	for (posAdjacente.n = posicaoCasa.n - 1; posAdjacente.n <= posicaoCasa.n
			+ 1; posAdjacente.n++) {
		casaAdjacente = getCasa(campo, posAdjacente);
		if (!(casaAdjacente->visivel))
			casasInvisiveis++;
	}

	posAdjacente.m = posicaoCasa.m + 1;
	for (posAdjacente.n = posicaoCasa.n - 1; posAdjacente.n <= posicaoCasa.n
			+ 1; posAdjacente.n++) {
		casaAdjacente = getCasa(campo, posAdjacente);
		if (!(casaAdjacente->visivel))
			casasInvisiveis++;
	}

	posAdjacente.m = posicaoCasa.m;
	posAdjacente.n = posicaoCasa.n - 1;
	casaAdjacente = getCasa(campo, posAdjacente);
	if (!(casaAdjacente->visivel))
		casasInvisiveis++;

	posAdjacente.n = posicaoCasa.n + 1;
	casaAdjacente = getCasa(campo, posAdjacente);
	if (!(casaAdjacente->visivel))
		casasInvisiveis++;

	return casasInvisiveis;
}
